﻿using Core.Log;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace Core.Utility.Helper
{
    /// <summary>
    /// HttpWeb请求类库
    /// </summary>
    public class HttpWebRequestHelper
    {
        /// <summary>
        /// 请求超时时间 单位：ms ; 默认值 2min
        /// </summary>
        public uint Timeout = 1 * 60 * 1000;

        /// <summary>
        /// 获取或设置写入或读取流时的超时（以毫秒为单位）。 默认值 5min
        /// </summary>
        public uint ReadWriteTimeout = 5 * 60 * 1000;

        #region GET请求
        /// <summary>
        /// 发送Get请求
        /// </summary>
        /// <param name="url">发起请求的URL地址</param>
        /// <param name="paramDictionary">参数字典</param>
        /// <param name="contentType">text/html | text/xml | application/json | application/x-www-form-urlencoded ；默认 text/html</param>
        /// <param name="encoding">响应编码 默认 Encoding.Default</param>
        /// <returns></returns>
        public HttpWebRequestResult Get(string url, Dictionary<string, string> paramDictionary, string contentType = "",Encoding? encoding = null)
        {
            return Get(url, GetHttpParam(paramDictionary),contentType, encoding);
        }

        /// <summary>
        /// 发送Get请求
        /// </summary>
        /// <param name="url">发起请求的</param>
        /// <param name="urlParam">URL参数字符串</param>
        /// <param name="contentType">text/html | text/xml | application/json | application/x-www-form-urlencoded ；默认 text/html</param>
        /// <param name="encoding">响应编码 默认 Encoding.UTF8</param>
        /// <returns></returns>
        public HttpWebRequestResult Get(string url, string urlParam = "",string contentType = "", Encoding? encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            HttpWebRequestResult res = new HttpWebRequestResult();

            StringBuilder sbMessage = new StringBuilder();
            sbMessage.AppendLine($"HttpWebRequest(GET);");
            sbMessage.AppendLine($"请求的URL地址为：\"{url}\" ;");
            sbMessage.AppendLine($"请求的URL参数为：");
            sbMessage.AppendLine($"{urlParam}");
            sbMessage.AppendLine($"请求的Content-Type为：\"{contentType}\" ;");
            sbMessage.AppendLine($"Encoding：\"{encoding}\" ;");
            try
            {
                if (!string.IsNullOrEmpty(urlParam))
                {
                    if (url.Contains("?"))
                    {
                        url = url + "&" + urlParam;
                    }
                    else
                    {
                        url = url + "?" + urlParam;
                    }
                }
                HttpWebRequest request = DefaultHttpWebRequest(url,"GET",contentType);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                sbMessage.AppendLine($"请求响应状态码：{response.StatusCode}");
                sbMessage.AppendLine($"请求响应状态码说明：{response.StatusDescription}");
                sbMessage.AppendLine($"请求响应的字符集：{response.CharacterSet}");
                res.HttpWebResponse = response;
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    Stream stream = response.GetResponseStream();
                    string responseString = string.Empty;
                    //设置编码的获取方式
                    //中文编码获取
                    //using (StreamReader reader = new StreamReader(stream, Encoding.GetEncoding("gb2312")))  
                    using (StreamReader reader = new StreamReader(stream, encoding))
                    {
                        responseString = UnicodeToString(reader.ReadToEnd());
                    }
                    //string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
                    //responseString = UnicodeToString(responseString);
                    res.Status = HttpWebRequestStatusEnum.Success;
                    res.ResponseContent = responseString;
                    sbMessage.AppendLine($"请求响应的内容：");
                    sbMessage.AppendLine($"{responseString}");
                }
                else
                {
                    res.Status = HttpWebRequestStatusEnum.Failed;
                    res.Message = $"{response.StatusCode}-{response.StatusDescription}";
                }
                res.HttpStatusCode = response.StatusCode;
                res.HttpStatusDescription = response.StatusDescription;
                res.Message = $"请求失败，请求结果为：\r\n{response.StatusCode}-{response.StatusDescription}";
                //_logService.InfomationExpand(sbMessage.ToString(), "HttpWebRequest", "GET请求信息");
                AppTrace.Logger.LogInformation(sbMessage.ToString(), new { 消息来源 = "HttpWebRequest", 附加信息 = "GET请求信息" });
                return res;
            }
            catch (Exception ex)
            {
                res.Status = HttpWebRequestStatusEnum.Exception;
                sbMessage.AppendLine($"异常信息为;");
                sbMessage.AppendLine($"{ExceptionMessageHelper.DetailMessage(ex)}");
                sbMessage.AppendLine($"----------");
                sbMessage.AppendLine($"{ExceptionMessageHelper.DetailInfo(ex)}");
                res.Message = sbMessage.ToString();
                res.Exception = ex;
                res.Message = $"请求异常，异常信息为\r\n{ExceptionMessageHelper.DetailMessage(ex)}";
                AppTrace.Logger.LogCritical(ex,sbMessage.ToString(),new { 消息来源 = "HttpWebRequest", 附加信息 = "GET请求异常" });
                return res;
            }
        }
        #endregion GET请求

        #region POST请求

        /// <summary>
        /// 发起POST请求
        /// </summary>
        /// <param name="url">请求的url地址</param>
        /// <param name="postDictionary">参数字典</param>
        /// <returns></returns>
        public HttpWebRequestResult Post(string url, Dictionary<string, string> postDictionary)
        {
            return Post(url, GetHttpParam(postDictionary));
        }


        /// <summary>
        /// 发起POST请求
        /// </summary>
        /// <param name="url">请求的url地址</param>
        /// <param name="postData">请求携带的数据</param>
        /// <param name="encoding">字符集编码 Encoding.UTF8</param>
        /// <param name="contentType"> text/html | text/xml | application/json | application/x-www-form-urlencoded；默认 application/json </param>
        /// <returns></returns>
        public HttpWebRequestResult Post(string url, string postData, string contentType = "application/json", Encoding? encoding = null)
        {
            HttpWebRequestResult res = new HttpWebRequestResult();
            StringBuilder sbMessage = new StringBuilder();
            sbMessage.AppendLine($"HttpWebRequest(POST);");
            sbMessage.AppendLine($"请求的URL地址为：\"{url}\" ;");
            sbMessage.AppendLine($"请求的Content-Type为：\"{contentType}\" ;");
            sbMessage.AppendLine($"请求的数据内容为：");
            sbMessage.AppendLine($"{postData}");
            try
            {
                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }
                HttpWebRequest request = DefaultHttpWebRequest(url,"POST",contentType);
                byte[] data = Encoding.UTF8.GetBytes(postData);
                request.ContentLength = data.Length;
                string responseString = string.Empty;
                using (Stream stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    sbMessage.AppendLine($"请求响应状态码：{response.StatusCode}");
                    sbMessage.AppendLine($"请求响应状态码说明：{response.StatusDescription}");
                    sbMessage.AppendLine($"请求响应的字符集：{response.CharacterSet}");
                    res.HttpWebResponse = response;
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        //using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("gb2312")))  
                        using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
                        {
                            responseString = UnicodeToString(reader.ReadToEnd());
                        }
                        res.Status = HttpWebRequestStatusEnum.Success;
                        res.ResponseContent = responseString;
                        sbMessage.AppendLine($"请求响应的内容：");
                        sbMessage.AppendLine($"{responseString}");
                    }
                    else
                    {
                        res.Status = HttpWebRequestStatusEnum.Failed;
                        res.Message = $"请求失败，请求结果为：\r\n{response.StatusCode}-{response.StatusDescription}";
                    }
                    res.HttpStatusCode = response.StatusCode;
                    res.HttpStatusDescription = response.StatusDescription;
                }
                AppTrace.Logger.LogInformation(sbMessage.ToString(), new { 消息来源 = "HttpWebRequest", 附加信息 = "POST请求信息" });
                return res;
            }
            catch (Exception ex)
            {
                res.Status = HttpWebRequestStatusEnum.Exception;
                sbMessage.AppendLine($"异常信息为;");
                sbMessage.AppendLine($"{ExceptionMessageHelper.DetailMessage(ex)}");
                sbMessage.AppendLine($"----------");
                sbMessage.AppendLine($"{ExceptionMessageHelper.DetailInfo(ex)}");
                res.Message = $"请求异常，异常信息为\r\n{ExceptionMessageHelper.DetailMessage(ex)}";
                res.Exception = ex;
                AppTrace.Logger.LogCritical(ex, sbMessage.ToString(), new { 消息来源 = "HttpWebRequest", 附加信息 = "POST请求异常" });
                return res;
            }
        }

        /// <summary>
        /// Post上传文件 multipart/form-data
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData">上传携带的数据</param>
        /// <param name="fileData">文件数据</param>
        /// <param name="fileName">文件名， 含文件名称及扩展名 aaa.mp3</param>
        /// <param name="strFileBoundary">文件边界符</param>
        /// <param name="ecd">编码</param>
        /// <returns></returns>
        public HttpWebRequestResult PostFileMultiart(string url, Dictionary<string, string> postData, string fileName, byte[] fileData, string strFileBoundary, Encoding? ecd = null)
        {

            if(ecd == null)
            {
                ecd = Encoding.UTF8;
            }
            HttpWebRequestResult res = new HttpWebRequestResult();
            try
            {
                HttpWebRequest request = DefaultHttpWebRequest(url,"POST");

                // 边界符  
                var boundary = $"---------------{strFileBoundary}x";
                // 边界符  
                var beginBoundary = ecd.GetBytes($"\r\n--{boundary}\r\n");
                // 最后的结束符  
                var endBoundary = ecd.GetBytes($"\r\n--{boundary}--\r\n");

                string contentType = $"multipart/form-data;charset=utf-8;boundary={boundary}";
                request.ContentType = contentType;

                string filePartHeader = $"Content-Disposition:form-data;name=\"{fileName}\";filename=\"{fileName}\"\r\nContent-Type:application/octet-stream\r\n\r\n";
                var headerbytes = ecd.GetBytes(filePartHeader);

                var memStream = new MemoryStream();
                memStream.Write(beginBoundary, 0, beginBoundary.Length);
                memStream.Write(headerbytes, 0, headerbytes.Length);

                //读取文件流
                memStream.Write(fileData, 0, fileData.Length);

                //内容行
                var contentLine = ecd.GetBytes("\r\n");
                memStream.Write(contentLine, 0, contentLine.Length);

                if (postData != null)
                {
                    // 写入字符串的Key  （写入Post传递的参数）
                    var stringKeyHeader = "\r\n--" + boundary +
                                           "\r\nContent-Disposition: form-data; name=\"{0}\"" +
                                           "\r\n\r\n{1}\r\n";
                    foreach (byte[] formitembytes in from string key in postData.Keys
                                                     select string.Format(stringKeyHeader, key, postData[key])
                                                         into formitem
                                                     select ecd.GetBytes(formitem))
                    {
                        memStream.Write(formitembytes, 0, formitembytes.Length);
                    }
                }

                // 写入最后的结束边界符  
                memStream.Write(endBoundary, 0, endBoundary.Length);

                //byte[] data = Encoding.UTF8.GetBytes(postData);
                request.Method = "POST";
                request.Accept = "*/*";
                request.ServicePoint.Expect100Continue = false;
                request.KeepAlive = true;
                //request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36";
                request.ContentLength = memStream.Length;
                string responseString = string.Empty;
                using (Stream stream = request.GetRequestStream())
                {
                    memStream.Position = 0;
                    var tempBuffer = new byte[memStream.Length];
                    memStream.Read(tempBuffer, 0, tempBuffer.Length);
                    memStream.Close();

                    stream.Write(tempBuffer, 0, tempBuffer.Length);
                    stream.Close();

                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    res.HttpWebResponse = response;
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        //using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("gb2312")))  
                        using (StreamReader reader = new StreamReader(response.GetResponseStream(), ecd))
                        {
                            responseString = UnicodeToString(reader.ReadToEnd());
                        }
                        res.Status = HttpWebRequestStatusEnum.Success;
                        res.ResponseContent = responseString;
                    }
                    else
                    {
                        res.Status = HttpWebRequestStatusEnum.Failed;
                        res.Message = $"{response.StatusCode}-{response.StatusDescription}";

                    }
                    res.HttpStatusCode = response.StatusCode;
                    res.HttpStatusDescription = response.StatusDescription;
                }
                return res;
            }
            catch (Exception ex)
            {
                res.Status = HttpWebRequestStatusEnum.Exception;
                StringBuilder sbMessage = new StringBuilder();
                sbMessage.AppendLine($"远程HTTP获取数据错误(POST);");
                sbMessage.AppendLine($"错误信息为;");
                sbMessage.AppendLine($"\t;{ExceptionMessageHelper.DetailMessage(ex)}");
                sbMessage.AppendLine($"请求的URL地址为：\"{url}\" ;");
                //sbMessage.AppendLine($"请求的Content-Type为：\"{contentType}\" ;");
                sbMessage.AppendLine($"请求的数据内容为：");
                sbMessage.AppendLine($"{GetHttpParam(postData)}");
                res.Message = sbMessage.ToString();
                res.Exception = ex;
                return res;
            }
        }

        /// <summary>
        /// PostUploadFile上传文件
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData"></param>
        /// <param name="fileName"></param>
        /// <param name="fileData"></param>
        /// <param name="strFileBoundary"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public HttpWebRequestResult PostUploadFile(string url, Dictionary<string, string> postData, string fileName, byte[] fileData, string strFileBoundary, Encoding? encoding = null)
        {
            HttpWebRequestResult res = new HttpWebRequestResult();           
            try
            {
                if(encoding == null)
                {
                    encoding = Encoding.UTF8;
                }

                string boundary = "---------------------------" + strFileBoundary + "x"; //+ DateTime.Now.Ticks.ToString("x");
                byte[] boundarybytes = encoding.GetBytes("\r\n--" + boundary + "\r\n");
                byte[] endbytes = encoding.GetBytes("\r\n--" + boundary + "--\r\n");
                //1.HttpWebRequest
                HttpWebRequest request = DefaultHttpWebRequest(url,"POST");
                request.ContentType = "multipart/form-data;charset=utf-8; boundary=" + boundary;
                //request.Credentials = CredentialCache.DefaultCredentials;
                CookieContainer cookieContainer = new CookieContainer();
                request.CookieContainer = cookieContainer;
                request.AllowAutoRedirect = true;
                //对发送的数据不使用缓存【重要、关键】
                request.AllowWriteStreamBuffering = false;
                request.SendChunked = true;//支持分块上传
                using (Stream stream = request.GetRequestStream())
                {
                    //1.1 key/value
                    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
                    if (postData != null)
                    {
                        foreach (string key in postData.Keys)
                        {
                            stream.Write(boundarybytes, 0, boundarybytes.Length);
                            string formitem = string.Format(formdataTemplate, key, postData[key]);
                            byte[] formitembytes = encoding.GetBytes(formitem);
                            stream.Write(formitembytes, 0, formitembytes.Length);
                        }
                    }
                    //1.2 file
                    string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";

                    stream.Write(boundarybytes, 0, boundarybytes.Length);
                    string header = string.Format(headerTemplate, strFileBoundary, fileName);
                    byte[] headerbytes = encoding.GetBytes(header);
                    stream.Write(headerbytes, 0, headerbytes.Length);
                    stream.Write(fileData, 0, fileData.Length);
                    //1.3 form end
                    stream.Write(endbytes, 0, endbytes.Length);
                }

                //2.WebResponse
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                res.HttpWebResponse = response;
                res.HttpStatusCode = response.StatusCode;
                res.HttpStatusDescription = response.StatusDescription;
                using (StreamReader stream = new StreamReader(response.GetResponseStream()))
                {
                    res.HttpWebResponse = response;
                    string responseString = string.Empty;
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        //using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("gb2312")))  
                        using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
                        {
                            responseString = UnicodeToString(reader.ReadToEnd());
                        }
                        res.Status = HttpWebRequestStatusEnum.Success;
                        res.ResponseContent = responseString;
                    }
                    else
                    {
                        res.Status = HttpWebRequestStatusEnum.Failed;
                        res.Message = $"{response.StatusCode}-{response.StatusDescription}";

                    }
                    return res;
                }
            }
            catch (Exception ex)
            {
                res.Status = HttpWebRequestStatusEnum.Exception;
                StringBuilder sbMessage = new StringBuilder();
                sbMessage.AppendLine($"远程HTTP获取数据错误(POST);");
                sbMessage.AppendLine($"错误信息为;");
                sbMessage.AppendLine($"\t;{ExceptionMessageHelper.DetailMessage(ex)}");
                sbMessage.AppendLine($"请求的URL地址为：\"{url}\" ;");
                //sbMessage.AppendLine($"请求的Content-Type为：\"{contentType}\" ;");
                sbMessage.AppendLine($"请求的数据内容为：");
                sbMessage.AppendLine($"{GetHttpParam(postData)}");
                res.Message = sbMessage.ToString();
                res.Exception = ex;
                return res;
            }
        }

        #endregion POST请求

        /// <summary>
        /// 获取默认HttpWebRequest
        /// </summary>
        /// <param name="url">请求的url地址</param>
        /// <param name="requestMethod">请求方式 GET,POST,PUT,DELETE,POST,默认GET；不区分大小写</param>
        /// <param name="contentType">text/html | text/xml | application/json | application/x-www-form-urlencoded ；默认 text/html</param>
        /// <returns></returns>
        [Obsolete("弃用该类库，建议使用RestSharp")]
        public HttpWebRequest DefaultHttpWebRequest(string url,string requestMethod = "GET",string contentType= "text/html")
        {
            requestMethod = requestMethod.ToUpper();
            if(requestMethod != "GET" && requestMethod != "PUT" && requestMethod != "DELETE" && requestMethod != "POST")
            {
                requestMethod = "GET";
            }
#pragma warning disable SYSLIB0014 // 类型或成员已过时
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
#pragma warning restore SYSLIB0014 // 类型或成员已过时
            request.Timeout = Convert.ToInt32(Timeout);
            request.ReadWriteTimeout = Convert.ToInt32(ReadWriteTimeout);
            request.Method = requestMethod;
            request.ContentType = contentType;
            request.Accept = "*/*";
            request.ServicePoint.Expect100Continue = false;
            request.KeepAlive = false;
            request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36";
            return request;
        }

        /// <summary>  
        /// Unicode转字符串  
        /// </summary>  
        /// <param name="source">经过Unicode编码的字符串</param>  
        /// <returns>正常字符串</returns>  
        public string UnicodeToString(string source)
        {
            return new Regex(@"\\u([0-9A-F]{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled).Replace(
                         source, x => string.Empty + Convert.ToChar(Convert.ToUInt16(x.Result("$1"), 16)));
        }

        /// <summary>
        /// 获取Url参数序列化后的字符串
        /// </summary>
        /// <param name="paramDic">需要序列化的字符串</param>
        /// <returns></returns>
        public string GetHttpParam(Dictionary<string, string> paramDic)
        {
            string strResult = string.Empty;
            if (paramDic != null)
            {
                StringBuilder builder = new StringBuilder();
                int i = 0;
                foreach (var item in paramDic)
                {
                    if (i > 0)
                    {
                        builder.Append("&");
                    }
                    builder.AppendFormat("{0}={1}", item.Key, HttpUtility.UrlEncode(item.Value));
                    i++;
                }
                strResult = builder.ToString();
            }
            return strResult;
        }

    }

    /// <summary>
    /// 
    /// </summary>
    public class HttpWebRequestResult
    {
        /// <summary>
        /// 请求状态
        /// </summary>
        [Description("请求状态")]
        public HttpWebRequestStatusEnum Status = HttpWebRequestStatusEnum.Failed;

        /// <summary>
        /// 请求信息，通常是指异常后返回的异常信息
        /// </summary>
        [Description("信息")]
        public string Message = "";

        /// <summary>
        /// 异常信息，通常是指异常后返回的异常类
        /// </summary>
        [Description("异常信息")]
        public Exception? Exception;

        /// <summary>
        /// 响应内容
        /// </summary>
        [Description("响应内容")]
        public string ResponseContent = "";

        /// <summary>
        /// 响应原型
        /// </summary>
        [Description("响应原型")]
        public HttpWebResponse? HttpWebResponse;

        /// <summary>
        /// Http状态码
        /// </summary>
        [Description("Http状态码")]
        public HttpStatusCode HttpStatusCode;


        /// <summary>
        /// Http状态码说明
        /// </summary>
        [Description("Http状态码说明")]
        public string? HttpStatusDescription;
    }

    /// <summary>
    /// HttpWeb请求状态
    /// </summary>
    public enum HttpWebRequestStatusEnum
    {
        /// <summary>
        /// 异常
        /// </summary>
        Exception = -1,
        /// <summary>
        /// 失败
        /// </summary>
        Failed = 0,
        /// <summary>
        /// 成功
        /// </summary>
        Success = 1,
    }

}
